#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import mmap
import ctypes

'''
sudo cat /proc/iomem

sudo python3
>>> from mmapio import *
>>> filepath = '/dev/mem'
>>> m = FileMmap(filepath, 0x100000000, 0x1000)
>>> print('{:X}'.format(m.readb(0x100)))
'''

class FileMmap(object):
    def __init__(self, filename, offset=0, size=0) -> None:
        try:
            self._fd = os.open(filename, os.O_RDWR | os.O_SYNC)
            self._map = mmap.mmap(self._fd, size,
                            flags=mmap.MAP_SHARED,
                            prot=mmap.PROT_READ | mmap.PROT_WRITE,
                            offset=offset)
        except PermissionError as e:
            raise PermissionError("Permisson denied: {}".format(e))

    def __del__(self):
        try:
            del self._map
            os.close(self._fd)
        except:
            pass

    def __getitem__(self, index):
        return self._map[index]

    def __setitem__(self, index, value):
        self._map[index] = value

    def __len__(self):
        return self._map.size()

    def __str__(self):
        return self._map.__str__()

    def __repr__(self):
        return self._map.__repr__()

    def __iter__(self):
        return self._map.__iter__()

    def __enter__(self):
        return self._map.__enter__()

    def __exit__(self, exc_type, exc_value, traceback):
        self._map.close()
        os.close(self._fd)

    def _read(self, offset:int, sz_type):
        pointer = ctypes.pointer(sz_type.from_buffer(self._map, offset))
        return pointer.contents.value

    def _write(self, val:int, offset:int, sz_type):
        pointer = ctypes.pointer(sz_type.from_buffer(self._map, offset))
        pointer.contents.value = val

    def readb(self, offset:int):
        return self._read(offset, ctypes.c_uint8)

    def readw(self, offset:int):
        return self._read(offset, ctypes.c_uint16)

    def readl(self, offset:int):
        return self._read(offset, ctypes.c_uint32)

    def readq(self, offset:int):
        return self._read(offset, ctypes.c_uint64)

    def writeb(self, val:int, offset:int):
        return self._write(val, offset, ctypes.c_uint8)

    def writew(self, val:int, offset:int):
        return self._write(val, offset, ctypes.c_uint16)

    def writel(self, val:int, offset:int):
        return self._write(val, offset, ctypes.c_uint32)

    def writeq(self, val:int, offset:int):
        return self._write(val, offset, ctypes.c_uint64)
